package network.handlers;

import java.io.IOException;

import state.Globals;
import state.group.GroupDispatcherState;
import managers.ElevatorManager;
import messages.BaseMessage;
import messages.GeneralAck;
import messages.MessageType;
import messages.observer.CurrentStateMessage;
import network.BaseModeHandler;
import network.ConnectionMode;
import network.managers.ObserverManager;
import network.server.ModeHandlerThread;

public class ObserverModeHandler extends BaseModeHandler {
    
    private int failCount = 0;
    
    public ObserverModeHandler(ModeHandlerThread parent)
    {
        super(parent);
    }

    @Override
    public ConnectionMode getMode()
    {
        return ConnectionMode.Observer;
    }

    @Override
    public boolean enterMode(byte id)
    {
        GeneralAck ack = new GeneralAck(MessageType.ObserverRegistrationMessage, true);
        
        try
        {
            parent.getRemoteSocket().getOutputStream().write(ack.toByteArray());
        }
        catch (IOException e)
        {
            Globals.getLogger().error("Error occurred while sending out the registration ack", e);
            return false;
        }
        
        boolean success = ObserverManager.getInstance().registerObserver(parent);
        
        if (success)
        {
        	Thread.currentThread().setName("Observer Handler " + Thread.currentThread().getId());
        }
        
        return success;
    }

    @Override
    public void exitMode()
    {
        ObserverManager.getInstance().deregisterObserver(parent);
    }

    @Override
    public ConnectionMode handleMessage(BaseMessage message)
    {
        if (message == null)
        {
            if (++failCount == 3)
            {
                return ConnectionMode.Closed;
            }
            else
            {
                return ConnectionMode.Observer;
            }
        }
        
        switch (message.getMessageType())
        {
            
            case DeregistrationMessage:
                return ConnectionMode.Closed;
            case CurrentStateRequest:
                failCount = 0;
                GroupDispatcherState state = ElevatorManager.getInstance().getDispatcherState();
                
                CurrentStateMessage returnMessage = null;
                
                synchronized(state.elevatorIPs)
                {
                    synchronized (state.elevatorData)
                    {
                        synchronized (state.hallCallMappings)
                        {
                            try
                            {
                                returnMessage = new CurrentStateMessage(state);
                            }
                            catch (IOException e)
                            {
                                Globals.getLogger().error("Error occurred while attempting to serialize the current state", e);
                                //this shouldn't really happen, if it does, we need to investigate it
                                //we'll REALLY know that this happens because I'm choosing not to handle
                                //the subsequent NullPointerException
                            }
                        }
                    }                
                }
                
                byte[] returnBuff = returnMessage.toByteArray();
                
                try
                {
                    parent.getRemoteSocket().getOutputStream().write(returnBuff);
                }
                catch (IOException e)
                {
                    Globals.getLogger().error("Error occurred while sending the current state to the observer", e);
                }
                
                return ConnectionMode.Observer;
            default:
                if (++failCount == 3)
                {
                    return ConnectionMode.Closed;
                }
                else
                {
                    return ConnectionMode.Observer;
                }
        }
    }

}
